package ch.fhnw.qualiscan.application.models;

import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;

import ch.fhnw.qualiscan.Config;
import ch.fhnw.qualiscan.core.Model;
import ch.fhnw.qualiscan.core.db.helpers.addUserToSurveyArgs;
import ch.fhnw.qualiscan.core.db.helpers.questionWeightArgs;
import ch.fhnw.qualiscan.core.db.pojos.Answer;
import ch.fhnw.qualiscan.core.db.pojos.BestPractice;
import ch.fhnw.qualiscan.core.db.pojos.Category;
import ch.fhnw.qualiscan.core.db.pojos.Confidence;
import ch.fhnw.qualiscan.core.db.pojos.CoordinatorRelevance;
import ch.fhnw.qualiscan.core.db.pojos.Domain;
import ch.fhnw.qualiscan.core.db.pojos.FinalEvaluation;
import ch.fhnw.qualiscan.core.db.pojos.Language;
import ch.fhnw.qualiscan.core.db.pojos.Question;
import ch.fhnw.qualiscan.core.db.pojos.QuestionText;
import ch.fhnw.qualiscan.core.db.pojos.Role;
import ch.fhnw.qualiscan.core.db.pojos.Stage;
import ch.fhnw.qualiscan.core.db.pojos.StageText;
import ch.fhnw.qualiscan.core.db.pojos.Survey;
import ch.fhnw.qualiscan.core.db.pojos.SurveyUserRole;
import ch.fhnw.qualiscan.core.db.pojos.User;
import ch.fhnw.qualiscan.core.helpers.Hash;

/**
 *
 * @author Kevin Mettenberger
 *
 */

public class SurveyModel extends Model {
       
		/**
		 * Gets a List containing all surveys in the database
		 * @return
		 */
		public List<Survey> getAllSurveys() {
			@SuppressWarnings("unchecked")
			List<Survey> surveyList = hibernateSession.getSession().createQuery("from Survey as survey")
			.list();
			
			return surveyList;
		}
	
       /**
         * Adds a new survey to the survey table. Also adds an entry in the association table "survey_user_role".
         *
         * @param survey the survey pojo to be saved.
         * @param user the user, that creates the survey.
         * @param role the role of the user, that creates the survey.
         * @return returns the created survey.
         */
        public Survey createSurvey(Survey survey, User user, Role role) {
        		//Save survey and make SurveyUserRole entry for creator
        		hibernateSession.getSession().save(survey);
                SurveyUserRole sur = new SurveyUserRole();
                sur.setRole(role);
                sur.setSurvey(survey);
                sur.setUser(user);
                hibernateSession.getSession().save(sur);
                hibernateSession.closeSession();
                
                return survey;
        }
        
        /**
         * Adds coordinator relevances (for questions) to the database.
         * @param survey The Survey, the relevances belong to.
         * @param questionWeights A List containing questionWeightArgs (information about the weight of each question).
         * @return A List containing the saved CoordinatorRelevance objects.
         */
        public List<CoordinatorRelevance> addQuestionWeights(Survey survey, List<questionWeightArgs> questionWeights) {
        	
        	
        	List<CoordinatorRelevance> crList = new ArrayList<CoordinatorRelevance>();
        	
            for (int i = 0; i < questionWeights.size(); i++) {
            	CoordinatorRelevance cr = new CoordinatorRelevance();
            	cr.setQuestion(questionWeights.get(i).getQuestion());
            	cr.setRelevance(questionWeights.get(i).getWeight());
            	cr.setSurvey(survey);
            	hibernateSession.getSession().save(cr);
            	
            	crList.add(cr);
            }
            
            hibernateSession.closeSession();
            
            return crList;
        }
       
        /**
         * Adds users and their roles for the survey to the association table "survey_user_role". It first checks if the user already exists in the database.
         * If the user doesn't exist, it creates a new one (with his email address).
         *
         * @param users An ArrayList containing addToSurveyArgs objects.
         * @param survey The Survey the users are being added to.
         * @return Returns a List of the added Users (SurveyUserRole Objects)
         */
       
        public List<SurveyUserRole> addUsersToSurvey(List<addUserToSurveyArgs> users, Survey survey) {
               
        		List<SurveyUserRole> surList = new ArrayList<SurveyUserRole>();
                for (int i = 0; i < users.size(); i++) {
                       
                        //Check if user exists in db
                        User user = (User)hibernateSession.getSession().createQuery("from User as user where user.email = :email")
                        .setParameter("email", users.get(i).getUser().getEmail())
                        .uniqueResult();
                       
                        //User doesn't exist in db
                        if (user == null) {
                                User u = users.get(i).getUser();
                                u.setRegdate(new Date());
                                Role role = new Role();
                                role.setId(Config.COORDINATOR_ID);
                                u.setRole(role);
                                hibernateSession.getSession().save(u);
                                
                                //Check if the entry doesn't exist
                                //SurveyUserRole sur = (SurveyUserRole)hibernateSession.getSession().createQuery("from SurveyUserRole as sur where sur.user = :user and sur.survey = :survey")
                                //.setParameter("user", u)
                                //.setParameter("survey", survey)
                                //.uniqueResult();
                                //if (sur == null) {
	                                SurveyUserRole sur = new SurveyUserRole();
	                                sur.setRole(users.get(i).getRole());
	                                sur.setUser(u);
	                                sur.setSurvey(survey);
	                               
	                                hibernateSession.getSession().save(sur);
	                                
	                                surList.add(sur);
                                //}
                        }
                        //User exists in db
                        else {
	                        	//Check if the entry doesn't exist
	                            //SurveyUserRole sur = (SurveyUserRole)hibernateSession.getSession().createQuery("from SurveyUserRole as sur where sur.user = :user and sur.survey = :survey")
	                            //.setParameter("user", user)
	                            //.setParameter("survey", survey)
	                            //.uniqueResult();
	                            //if (sur == null) {
	                                SurveyUserRole sur = new SurveyUserRole();
	                                sur.setRole(users.get(i).getRole());
	                                sur.setUser(user);
	                                sur.setSurvey(survey);
	                               
	                                hibernateSession.getSession().save(sur);
	                                
	                                surList.add(sur);
	                            //}
                        }
                }
               
                hibernateSession.closeSession();
                
                return surList;
        }
       
        /**
         * Returns the question texts for a specified domain and language.
         *
         * @param language The language one wants the questions in.
         * @param domain The domain of the requested questions.
         * @return Returns an ArrayList containing QuestionText objects.
         */
        public ArrayList<QuestionText> getQuestionTextsByDomain(Language language, Domain domain) {
               
                ArrayList<QuestionText> questionTexts = new ArrayList<QuestionText>();
               
                Domain d = (Domain)hibernateSession.getSession().load(Domain.class, domain.getId());

                @SuppressWarnings("unchecked")
				Iterator<Question> questionIterator = d.getQuestions().iterator();

                while(questionIterator.hasNext()) {
                        Question q = (Question)questionIterator.next();
                       
                        QuestionText qt = (QuestionText)hibernateSession.getSession().createQuery("from QuestionText as qt where qt.question.id = :qid and qt.language.id = :lid")
                        .setParameter("lid", language.getId())
                        .setParameter("qid",q.getId())
                        .uniqueResult();
                       
                        questionTexts.add(qt);
               
                }
               
                return questionTexts;
               
        }
        
        /**
         * Gets a survey by Id. Returns null if survey does not exist.
         * @param id of survey
         * @return Survey Object
         */
        public Survey getSurveyById(int id) {

        	Survey survey = (Survey) hibernateSession.getSession().createQuery("from Survey as survey where survey.id = :id")
        	.setParameter("id", id)
        	.uniqueResult();

        	return survey;
        }
        
        /**
         * Gets a SurveyUserRole object by Id. Returns null if SurveyUserRole object does not exist.
         * @param id of SurveyUserRole
         * @return SurveyUserRole Object
         */
        public SurveyUserRole getSurById(int id) {

        	SurveyUserRole sur = (SurveyUserRole) hibernateSession.getSession().createQuery("from SurveyUserRole as sur where sur.id = :id")
        	.setParameter("id", id)
        	.uniqueResult();

        	return sur;
        }
        
        /**
         * 
         * @param thirdParameter SurveyUserRole id and hash code to double check.
         * @return returns the SurveyUserRole object if the hash is equal, else null.
         */
		public SurveyUserRole getSurveyUserRoleByHash(String thirdParameter) {

			String[] tokens = thirdParameter.split("@");
			
			if(tokens.length != 2) {
				return null;
			}
			
			int id;
			String hash = tokens[1];
			
			try {
				id = Integer.parseInt(tokens[0]);
			} catch(NumberFormatException e) {
				return null;
			}
			
			SurveyUserRole sur = (SurveyUserRole) hibernateSession.getSession().createQuery("from SurveyUserRole as sur where sur.id = :id")
			.setParameter("id", id)
			.uniqueResult();
			
			if(sur == null) {
				return null;
			}
			
			// generate hash
			Hash myHash = new Hash();
			String generatedHash = myHash.generateSurHash(sur.getSurvey().getId(), sur.getUser().getId(), sur.getRole().getId());
			
			//System.out.println("model -> my hash" + generatedHash);
			//System.out.println("model -> your hash" + hash);
			
			// compare hashes
			if(generatedHash.equals(hash)) {
				return sur;
			} else {
				return null;
			}
			
		}
		
		/**
		 * Deletes a participant from survey. 
		 * @param sur SurveyUserRole object
		 * @return true
		 */
		public boolean deleteParticipant(SurveyUserRole sur) {
			
			//Answers
			List<Answer> answerList = hibernateSession.getSession().createQuery("from Answer where sur = :sur")
			.setParameter("sur", sur)
			.list();
			
			for (int a = 0; a < answerList.size(); a++ ) {
				Question q = (Question) hibernateSession.getSession().get(Question.class, answerList.get(a).getQuestion().getId());
				q.removeAnswer(answerList.get(a));
				hibernateSession.getSession().merge(q);
				hibernateSession.getSession().delete(answerList.get(a));
			}
			
			//Best Practices
			List<BestPractice> bpList = hibernateSession.getSession().createQuery("from BestPractice where sur = :sur")
			.setParameter("sur", sur)
			.list();
			
			for (int b = 0; b < bpList.size(); b++) {
				Domain d = (Domain) hibernateSession.getSession().get(Domain.class, bpList.get(b).getDomain().getId());
				d.removeBestpractice(bpList.get(b));
				hibernateSession.getSession().merge(d);
				hibernateSession.getSession().delete(bpList.get(b));
			}
			
			//Confidence
			List<Confidence> confidenceList = hibernateSession.getSession().createQuery("from Confidence where sur = :sur")
			.setParameter("sur", sur)
			.list();
			
			for (int c = 0; c < confidenceList.size(); c++) {
				Domain d = (Domain) hibernateSession.getSession().get(Domain.class, confidenceList.get(c).getDomain().getId());
				d.removeConfidence(confidenceList.get(c));
				hibernateSession.getSession().merge(d);
				hibernateSession.getSession().delete(confidenceList.get(c));
			}
			
			//Final Evaluation
			List<FinalEvaluation> evaluationList = hibernateSession.getSession().createQuery("from FinalEvaluation where sur = :sur")
			.setParameter("sur", sur)
			.list();
			
			for (int e = 0; e < evaluationList.size(); e++) {
				hibernateSession.getSession().delete(evaluationList.get(e));
			}
			
			hibernateSession.getSession().delete(sur);
			hibernateSession.closeSession();
			return true;
		}
		
		/**
		 * Updates a SurveyUserRole object.
		 * @param sur the SurveyUserRole object to be updated
		 * @return returns true if the survey was updated succesfully
		 */
		public boolean updateSur(SurveyUserRole sur) {
			hibernateSession.getSession().merge(sur);
			hibernateSession.getSession().flush();
			return true;
		}
		
		/**
		 * Checks if a user is a participant in a survey.
		 * @param survey the survey
		 * @param user the user
		 * @return returns true if the user is a participant, else false.
		 */
		public boolean userParticipatesSurvey(Survey survey, User user) {
			List<SurveyUserRole> surList = hibernateSession.getSession().createQuery("from SurveyUserRole as sur where survey = :survey and user = :user")
			.setParameter("survey", survey)
			.setParameter("user", user)
			.list();
			
			if (surList.size() > 0) {
				return true;
			}
			
			return false;
		}
		
		
		/**
		 * Returns a SurveyUserObject identified by 
		 * @param survey the survey
		 * @param user the user
		 * @return returns the SurveyUserRole object
		 */
		public SurveyUserRole getSurBySurveyAndUser(Survey survey, User user) {
			SurveyUserRole sur = (SurveyUserRole)hibernateSession.getSession().createQuery("from SurveyUserRole as sur where survey = :survey and user = :user")
			.setParameter("survey", survey)
			.setParameter("user", user)
			.uniqueResult();
			
			return sur;
		}
		
		
		/**
		 * Returns the participants of a survey (EXCEPT: COORDINATOR, EXPERT!)
		 * @param survey the survey
		 * @return a List of particpants
		 */
		public List<SurveyUserRole> getParticipantsOfSurvey(Survey survey) {
			Role roleCoordinator = new Role();
			roleCoordinator.setId(Config.COORDINATOR_ID);
			
			Role roleExpert = new Role();
			roleExpert.setId(Config.EXPERT_ID);
			
			@SuppressWarnings("unchecked")
			List<SurveyUserRole> surList = hibernateSession.getSession().createQuery("from SurveyUserRole as sur where survey = :survey and role != :roleC and role != :roleE")
			.setParameter("survey", survey)
			.setParameter("roleC", roleCoordinator)
			.setParameter("roleE", roleExpert)
			.list();
			
			
			List<User> userList = new ArrayList<User>();
			for (int i = 0; i < surList.size(); i++) {
				userList.add(surList.get(i).getUser());
			}
			
			return surList;
		}
		
		/**
		 * Returns a list of SurveyUserRole objects for a specified Survey
		 * @param survey the survey
		 * @return List containing SurveyUserRole objects.
		 */
		public List<SurveyUserRole> getSurListForSurvey(Survey survey) {
			@SuppressWarnings("unchecked")
			List<SurveyUserRole> surList = hibernateSession.getSession().createQuery("from SurveyUserRole as sur where survey = :survey")
			.setParameter("survey", survey)
			.list();
			
			return surList;
		}
		
		
		/**
		 * Removes a user from a survey (Deletes his answers, best practices, and confidence)
		 * @param sur
		 */
		public void removeUserFromSurvey(SurveyUserRole sur) {
			//Delete answers
			@SuppressWarnings("unchecked")
			List<Answer> answerList = hibernateSession.getSession().createQuery("from Answer as a where user = :user and survey = :survey")
			.setParameter("user", sur.getUser())
			.setParameter("survey", sur.getSurvey())
			.list();
			
			for (int i = 0; i < answerList.size(); i++) {
				Question question = answerList.get(i).getQuestion();
				question.removeAnswer(answerList.get(i));
				hibernateSession.getSession().delete(answerList.get(i));
			}
			
			//Delete best practices
			@SuppressWarnings("unchecked")
			List<BestPractice> bpList = hibernateSession.getSession().createQuery("from BestPractice as bp where user = :user and survey = :survey")
			.setParameter("user", sur.getUser())
			.setParameter("survey", sur.getSurvey())
			.list();
			
			for (int b = 0; b < bpList.size(); b++) {
				Domain domain = bpList.get(b).getDomain();
				domain.removeBestpractice(bpList.get(b));
				hibernateSession.getSession().delete(bpList.get(b));
			}
			
			//Delete confidence
			@SuppressWarnings("unchecked")
			List<Confidence> confidenceList = hibernateSession.getSession().createQuery("from Confidence as c where user = :user and survey = :survey")
			.setParameter("user", sur.getUser())
			.setParameter("survey", sur.getSurvey())
			.list();
			
			for (int c = 0; c < confidenceList.size(); c++) {
				Domain domain = confidenceList.get(c).getDomain();
				domain.removeConfidence(confidenceList.get(c));
				hibernateSession.getSession().delete(confidenceList.get(c));
			}
			
			//Delete SurveyUserRole
			hibernateSession.getSession().delete(sur);
			hibernateSession.getSession().flush();
			hibernateSession.closeSession();
		}
		
		/**
		 * Gets the stage text for a specified stage and language
		 * @param stage the stage
		 * @param language the language
		 * @return a StageText object
		 */
		public StageText getStageText(Stage stage, Language language) {
			StageText st = (StageText) hibernateSession.getSession().createQuery("from StageText as st where stage = :stage and language = :language")
			.setParameter("stage", stage)
			.setParameter("language", language)
			.uniqueResult();
			
			return st;
		}
		
		/**
		 * Checks if there is a next stage
		 * @param stage the current stage
		 * @return true if there is a next stage, else false.
		 */
		public boolean isNextStage(Stage stage) {
			int nextId = stage.getId()+1;
			Stage nextStage = (Stage) hibernateSession.getSession().get(Stage.class, nextId);
			if(nextStage != null) {
				return true;
			}
			return false;
		}
		
		/**
		 * Gets the next stage
		 * @param stage the current stage
		 * @return next stage
		 */
		public Stage getNextStage(Stage stage) {
			int nextId = stage.getId()+1;
			Stage nextStage = (Stage) hibernateSession.getSession().get(Stage.class, nextId);

			return nextStage;
		}
		
		/**
		 * Sets the stage of a survey to the next level.
		 * @param stage the current stage
		 * @param survey the survey
		 * @return returns the updated survey.
		 */
		public Survey changeToNextStage(Stage stage, Survey survey) {
			int nextId = stage.getId()+1;

			
			Stage nextStage = (Stage) hibernateSession.getSession().get(Stage.class, nextId);
			
			if(nextStage != null) {	
				survey.setStage(nextStage);
				hibernateSession.getSession().merge(survey);
				hibernateSession.getSession().flush();

			}
			return survey;
		}
		
		/**
		 * Returns a list of SurveyUserRole objects for a specified user.
		 * @param user the user
		 * @return list containing SurveyUserRole objects.
		 */
		public List<SurveyUserRole> getSurListByUser(User user) {
			@SuppressWarnings("unchecked")
			List<SurveyUserRole> surList = hibernateSession.getSession().createQuery("from SurveyUserRole as sur where user = :user")
			.setParameter("user", user)
			.list();
			
			return surList;
		}
		
		/**
		 * Updates the changes to a survey object to the database
		 * @param survey the changed survey object.
		 */
		public void updateSurvey(Survey survey) {
			hibernateSession.getSession().merge(survey);
			hibernateSession.closeSession();
		}
		 		
		
		/**
		 * This function checks if a user is a participant of a specific survey
		 * @param userId user id
		 * @param surveyId survey id
		 * @return true if user is participant, false if user is not participating
		 */
		public boolean isParticipant(int userId, int surveyId) {
			SurveyUserRole sur = (SurveyUserRole) hibernateSession.getSession().createQuery("from SurveyUserRole as sur WHERE user.id = :user AND survey.id = :survey")
			.setParameter("user", userId)
			.setParameter("survey", surveyId)
			.uniqueResult();
			
			if(sur == null) {
				return false;
			} else {
				return true;
			}
			
		}
		
		/**
		 * Returns a list of all categories stored in the db.
		 * @return List containing Category objects
		 */
		public List<Category> getCategories() {
			@SuppressWarnings("unchecked")
			List<Category> categoryList = hibernateSession.getSession().createQuery("from Category")
			.list();
			
			return categoryList;
		}
		
		/**
		 * Returns a list containing the coordinators of a survey
		 * @param survey the survey
		 * @return List containing user pojos (coordinators of the survey)
		 */
		public List<SurveyUserRole> getCoordinatorsOfSurvey(Survey survey) {			
			Role role = new Role();
			role.setId(config.COORDINATOR_ID);
			
			List<SurveyUserRole> surList = hibernateSession.getSession().createQuery("from SurveyUserRole as sur WHERE survey = :survey AND role = :role")
			.setParameter("survey", survey)
			.setParameter("role", role)
			.list();
			
			return surList;
	
		}
		
		/**
		 * Saves or updates a final evaluation in the database.
		 * @param title Title of the final evaluation
		 * @param text Text of the final evaluation
		 * @param sur SurveyUserRole object
		 * 
		 */
		public void saveFinalEvaluation(String title, String text, SurveyUserRole sur) {
			
			Date date = new Date();
			
			if (!title.equals("") || !text.equals("")) {
				FinalEvaluation finalEvaluation = (FinalEvaluation) hibernateSession.getSession().createQuery("from FinalEvaluation WHERE user = :user AND survey = :survey")
				.setParameter("user", sur.getUser())
				.setParameter("survey", sur.getSurvey())
				.uniqueResult();
				
				if(finalEvaluation == null) {
					finalEvaluation = new FinalEvaluation();
					finalEvaluation.setTitle(title);
					finalEvaluation.setFinalevaluation(text);
					finalEvaluation.setEditdate(date);
					finalEvaluation.setPublicpost(1);
					finalEvaluation.setUser(sur.getUser());
					finalEvaluation.setSurvey(sur.getSurvey());
					finalEvaluation.setSur(sur);
					
					hibernateSession.getSession().save(finalEvaluation);
				}
				else {
					finalEvaluation.setEditdate(date);
					finalEvaluation.setTitle(title);
					finalEvaluation.setFinalevaluation(text);
					
					hibernateSession.getSession().update(finalEvaluation);
				}
			}
			
			hibernateSession.getSession().flush();
			
		}
		/**
		 * Gets the final evaluation for a survey and user.
		 * @param sur SurveyUserRole object.
		 * @return
		 */
		public FinalEvaluation getFinalEvaluation(SurveyUserRole sur) {
			FinalEvaluation finalEvaluation = (FinalEvaluation) hibernateSession.getSession().createQuery("from FinalEvaluation WHERE sur = :sur")
			.setParameter("sur", sur)
			.uniqueResult();
			
			return finalEvaluation;
		}
		
		
		
		
}

